#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"
#define BUFSIZE 1024

enum
{
  STATE_R = 1, // reading
  STATE_W,     // writing
  STATE_EX,    // exception
  STATE_T      // terminated
};

struct fsm_st
{
  int state;
  int sfd; // source fd
  int dfd; // destination fd
  int len; // read len
  char buf[BUFSIZE];
  int pos;
};

static void fsm_driver(struct fsm_st *fsm)
{
  switch (fsm->state) {
    case STATE_R:
	    fsm->len = read(fsm->sfd, fsm->buf, BUFSIZE);
	    if (fsm->len == 0) {
	      fsm->state = STATE_T;
	    } else if (fsm->len < 0) {
	      if (errno == EAGAIN) {
	        fsm->state = STATE_R;
	      } else {
	        fsm->state = STATE_EX;
	      }
	    } else {
	      fsm->state = STATE_W;
	      fsm->pos = 0;
	    }
	    break;
    case STATE_W:
	    int n = write(fsm->dfd, fsm->buf + fsm->pos, fsm->len);
	    if (n < 0) {
	      if (errno == EAGAIN) {
	        fsm->state = STATE_W;
	      } else {
	        fsm->state = STATE_EX;
	      }
	    } else if (n < fsm->len) {
	      fsm->state = STATE_W;
	      fsm->len -= n;
	      fsm->pos += n;
	    } else {
	      fsm->state = STATE_R;
	    }
	    break;
    case STATE_EX:
	    perror("exception");
	    fsm->state = STATE_T;
	    break;
    case STATE_T:
	    /* */
	    break;
    default:
	    abort();
	    break;

  }
}

static void relay(int fd1, int fd2)
{
  int fd1_save, fd2_save;
  struct fsm_st fsm12; // read left write right
  struct fsm_st fsm21; // read right write left

  fd1_save = fcntl(fd1, F_GETFL);
  fcntl(fd1, F_SETFL, fd1_save | O_NONBLOCK);
  fd2_save = fcntl(fd2, F_GETFL);
  fcntl(fd2, F_SETFL, fd2_save | O_NONBLOCK);

  fsm12.state = STATE_R;
  fsm12.sfd = fd1;
  fsm12.dfd = fd2;
  fsm21.state = STATE_R;
  fsm21.sfd = fd2;
  fsm21.dfd = fd1;

  // rl -> wr -> rr -> wl -> ...
  while (fsm12.state != STATE_T || fsm21.state != STATE_T) {
    fsm_driver(&fsm12);
    fsm_driver(&fsm21);
  }

  // recovery original state
  fcntl(fd1, F_SETFL, fd1_save);
  fcntl(fd2, F_SETFL, fd2_save);
}

int main()
{
  int fd1, fd2;
  if ((fd1 = open(TTY1, O_RDWR)) < 0) {
    perror("open");
    exit(1);
  }
  write(fd1, "TTY1\n", 5);
  if ((fd2 = open(TTY2, O_RDWR | O_NONBLOCK)) < 0) {
    close(fd1);
    perror("open");
    exit(1);
  }
  write(fd2, "TTY2\n", 5);
  printf("%d\n%d\n", fd1, fd2);

  // the mid engine between fd1 and fd2
  relay(fd1, fd2);

  close(fd1);
  close(fd2);
  exit(0);
}
